﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.Input;
using Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter;
using Notifications.Wpf;
using PaddleOCRSharp;
using VideoTools.service;
using VideoTools.SubtitleEdit.Common;
using VideoTools.SubtitleEdit.SubtitleFormats;
using VideoTools.Tasks;
using VideoTools.Util;

namespace VideoTools.Commands
{
    public class OcrSupSubtitleCommands : NotifyPropertyBase
    {
        private static volatile OcrSupSubtitleCommands _instance;
        private static readonly object SyncRoot = new object();
        IImageSubtitleService subtitleService;
        PaddleOCREngine paddleEngine = null;
        private static BitmapImage NULL_IMAGE = null;
        public static OcrSupSubtitleCommands Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (SyncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new OcrSupSubtitleCommands();
                        }
                    }
                }

                return _instance;
            }
        }

        private OcrSupSubtitleCommands()
        {
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
            OCRParameter oCRParameter = new OCRParameter();
            oCRParameter.use_gpu = true;
            paddleEngine = new PaddleOCREngine(null, oCRParameter);
            FileInfo f = new FileInfo("./nullimage.png");
            NULL_IMAGE = new BitmapImage(new Uri(f.FullName));
        }

        private string _TimeLeave;

        public string TimeLeave
        {
            get => _TimeLeave;
            set => SetProperty(ref _TimeLeave, value);
        }

        private string _ImageDesc;

        public string ImageDesc
        {
            get => _ImageDesc;
            set => SetProperty(ref _ImageDesc, value);
        }
        private string _OcrFile;

        public string OcrFile
        {
            get => _OcrFile;
            set => SetProperty(ref _OcrFile, value);
        }
        private SubtitleEncode _SubtitleEncode;

        public SubtitleEncode SubtitleEncode
        {
            get => _SubtitleEncode;
            set => SetProperty(ref _SubtitleEncode, value);
        }

        private bool _TradToSimple;

        public bool TradToSimple
        {
            get => _TradToSimple;
            set => SetProperty(ref _TradToSimple, value);
        }

        private bool _OcrSupEnable;

        public bool OcrSupEnable
        {
            get => _OcrSupEnable;
            set => SetProperty(ref _OcrSupEnable, value);
        }
        private bool _ClearListEnable;

        public bool ClearListEnable
        {
            get => _ClearListEnable;
            set => SetProperty(ref _ClearListEnable, value);
        }

        private BitmapImage _SubtitleImage;

        public BitmapImage SubtitleImage
        {
            get => _SubtitleImage;
            set => SetProperty(ref _SubtitleImage, value);
        }
        private Paragraph _ItemToScrollTo;

        public Paragraph ItemToScrollTo
        {
            get => _ItemToScrollTo;
            set => SetProperty(ref _ItemToScrollTo, value);
        }

        private ICommand _ClearListCommand;
        private ICommand _OpenSupFileCommand;
        private ICommand _OcrSupFileCommand;
        private ICommand _ClickOneSupCommand;
        private ICommand _SaveSubtitleImageCommand;
        private ICommand _SaveSubtitleCommand;
        public ICommand SaveSubtitleCommand =>
            _SaveSubtitleCommand
            ?? (
                _SaveSubtitleCommand = new RelayCommand<object>(o =>
                {
                    saveSubtitle();
                })
            );

        public ICommand SaveSubtitleImageCommand =>
            _SaveSubtitleImageCommand
            ?? (
                _SaveSubtitleImageCommand = new RelayCommand<object>(o =>
                {
                    if (o == null)
                        return;
                    if (
                        subtitleService == null
                        || SubtitleEncode == null
                        || SubtitleEncode.Paragraphs == null
                        || SubtitleEncode.Paragraphs.Count == 0
                    )
                        return;

                    if (o is string a && a.Equals("All"))
                    {
                        //保存全部字幕图片
                        int imagesSavedCount = 0;
                        var sb = new StringBuilder();
                        sb.AppendLine("<!DOCTYPE html>");
                        sb.AppendLine("<html>");
                        sb.AppendLine("<head>");
                        sb.AppendLine("   <meta charset=\"UTF-8\" />");
                        sb.AppendLine("   <title>Subtitle images</title>");
                        sb.AppendLine("</head>");
                        sb.AppendLine("<body>");
                        string path = OcrFile.Replace(Path.GetExtension(OcrFile), "");
                        if (!Directory.Exists(path))
                        {
                            Directory.CreateDirectory(path);
                        }
                        for (int i = 0; i < SubtitleEncode.Paragraphs.Count; i++)
                        {
                            var bmp = subtitleService.subtitleBitmap(i);
                            if (bmp != null)
                            {
                                var fileName = string.Format(
                                    CultureInfo.InvariantCulture,
                                    "{0:0000}.png",
                                    i + 1
                                );
                                var filePath = Path.Combine(path, fileName);
                                bmp.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
                                imagesSavedCount++;
                                var p = SubtitleEncode.Paragraphs[i];
                                sb.AppendFormat(
                                    CultureInfo.InvariantCulture,
                                    "#{3}:{0}->{1}<div style='text-align:center'><img src='{2}' />",
                                    p.StartTime.ToShortString(),
                                    p.EndTime.ToShortString(),
                                    fileName,
                                    i + 1
                                );
                                if (!string.IsNullOrEmpty(p.Text))
                                {
                                    var backgroundColor = ColorTranslator.ToHtml(Color.WhiteSmoke);
                                    var text = WebUtility
                                        .HtmlEncode(
                                            p.Text.Replace("<i>", "@1__").Replace("</i>", "@2__")
                                        )
                                        .Replace("@1__", "<i>")
                                        .Replace("@2__", "</i>")
                                        .Replace(Environment.NewLine, "<br />");
                                    sb.Append("<br /><div style='font-size:22px; background-color:")
                                        .Append(backgroundColor)
                                        .Append("'>")
                                        .Append(text)
                                        .Append("</div>");
                                }

                                sb.AppendLine("</div><br /><hr />");
                                bmp.Dispose();
                            }
                        }

                        sb.AppendLine("</body>");
                        sb.AppendLine("</html>");
                        var htmlFileName = Path.Combine(path, "index.html");
                        System.IO.File.WriteAllText(htmlFileName, sb.ToString(), Encoding.UTF8);
                        var content = new NotificationContent()
                        {
                            Title = "图片已保存(点击打开)",
                            Type = NotificationType.Information,
                            Message = path,
                        };
                        AppManager.Instance.NotificationManager.Show(
                            content,
                            onClick: () =>
                            {
                                Utils.OpenUrlUsingEdge(htmlFileName);
                            }
                        );
                    }
                    else if (o is System.Windows.Controls.Image image && image != null)
                    {
                        // 假设你的Image控件名为myImage
                        BitmapSource bitmap = image.Source as BitmapSource;
                        if (bitmap != null && !string.IsNullOrEmpty(ImageDesc))
                        {
                            string number = ImageDesc;
                            number = number.Split(" ")[1];
                            number = number.Split("/")[0];
                            string path = Path.ChangeExtension(OcrFile, $"_{number}.png");

                            using (FileStream stream = File.Create(path))
                            {
                                PngBitmapEncoder encoder = new PngBitmapEncoder();
                                encoder.Frames.Add(BitmapFrame.Create(bitmap));
                                encoder.Save(stream);
                                var content = new NotificationContent()
                                {
                                    Title = "图片已保存(点击打开)",
                                    Type = NotificationType.Information,
                                    Message = path,
                                };
                                AppManager.Instance.NotificationManager.Show(
                                    content,
                                    onClick: () =>
                                    {
                                        Process.Start(
                                            new ProcessStartInfo(path) { UseShellExecute = true }
                                        );
                                    }
                                );
                            }
                        }
                    }
                })
            );

        public ICommand ClickOneSupCommand =>
            _ClickOneSupCommand
            ?? (
                _ClickOneSupCommand = new RelayCommand<object>(o =>
                {
                    if (o == null)
                        return;
                    System.Windows.Controls.ListView listView = (System.Windows.Controls.ListView)o;
                    int selectedIndex = listView.SelectedIndex;
                    if (selectedIndex < 0)
                    {
                        return;
                    }
                    int numberOfImages = subtitleService.GetSubtitleCount();
                    Bitmap bmp = subtitleService.subtitleBitmap(selectedIndex);
                    SubtitleImage = Utils.BitmapToBitmapImage(bmp);
                    ImageDesc = string.Format(
                        "字幕图像 {0}/{1} - {2}x{3}",
                        selectedIndex + 1,
                        numberOfImages,
                        bmp.Width,
                        bmp.Height
                    );
                    if (!string.IsNullOrEmpty(SubtitleEncode.Paragraphs[selectedIndex].Text))
                        return;
                    OCRResult ocrResult = paddleEngine.DetectText(bmp);
                    var txt = "";
                    if (ocrResult.TextBlocks.Count > 0)
                    {
                        List<PaddleOCRSharp.TextBlock> list = ocrResult.TextBlocks;
                        if (list.Count == 1)
                        {
                            txt = list[0].Text;
                        }
                        else
                        {
                            for (int i = 0; i < list.Count - 1; i++)
                            {
                                txt += list[i].Text + " ";
                            }
                            txt += list[list.Count - 1].Text;
                        }
                    }
                    if (TradToSimple)
                    {
                        txt = ChineseConverter.Convert(
                            txt,
                            ChineseConversionDirection.TraditionalToSimplified
                        );
                    }
                    SubtitleEncode.Paragraphs[selectedIndex].Text = txt;
                    listView.Items.Refresh();
                })
            );

        public ICommand ClearListCommand =>
            _ClearListCommand
            ?? (
                _ClearListCommand = new RelayCommand<object>(o =>
                {
                    OcrFile = string.Empty;
                    SubtitleEncode = new SubtitleEncode();
                    System.Windows.Controls.Image subtitleImage = (System.Windows.Controls.Image)o;
                    SubtitleImage = NULL_IMAGE;
                    OcrSupSubtitleTaskQueueManager.Instance.ClearAllTask();
                    ImageDesc = null;
                })
            );

        public ICommand OpenSupFileCommand =>
            _OpenSupFileCommand
            ?? (
                _OpenSupFileCommand = new RelayCommand<object>(o =>
                {
                    if (o == null)
                        return;
                    System.Windows.Controls.ListView listView = (System.Windows.Controls.ListView)o;
                    OcrFile = string.Empty;
                    System.Windows.Forms.OpenFileDialog openFileDialog =
                        new System.Windows.Forms.OpenFileDialog();
                    string filter = "sup字幕文件(*.sup,*.sub)|*.sup;*.sub";
                    openFileDialog.Filter = filter;
                    openFileDialog.Multiselect = false;
                    openFileDialog.RestoreDirectory = true;
                    openFileDialog.FilterIndex = 1;
                    ImageDesc = null;
                    SubtitleImage = NULL_IMAGE;
                    if (openFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        OcrFile = openFileDialog.FileName;

                        if (!checkFiles())
                        {
                            OcrFile = string.Empty;
                            return;
                        }
                        OcrSupEnable = true;
                        ClearListEnable = true;

                        var log = new StringBuilder();

                        if (OcrFile.ToLower().EndsWith(".sub"))
                        {
                            subtitleService = new SubImageSubtitleService(OcrFile);
                        }
                        else if (OcrFile.ToLower().EndsWith(".sup"))
                        {
                            subtitleService = new SupImageSubtitleService(OcrFile);
                        }
                        SubtitleEncode = subtitleService.parseSubtitle();

                        if (
                            SubtitleEncode == null
                            || SubtitleEncode.Paragraphs == null
                            || SubtitleEncode.Paragraphs.Count == 0
                        )
                        {
                            var content = new NotificationContent()
                            {
                                Title = "字幕文件为空",
                                Type = NotificationType.Error,
                                Message = OcrFile,
                            };
                            AppManager.Instance.NotificationManager.Show(content);
                            return;
                        }
                        SubtitleEncode.Renumber();
                        FixShortDisplayTimes(SubtitleEncode);
                        foreach (var p in SubtitleEncode.Paragraphs)
                        {
                            OcrSupSubtitleTaskItem taskItem = new OcrSupSubtitleTaskItem()
                            {
                                Paragraph = p,
                            };
                            OcrSupSubtitleTaskQueueManager.Instance.AddTask(
                                taskItem,
                                listView,
                                TaskAction
                            );
                            p.Text = string.Empty;
                        }
                    }
                    else
                    {
                        return;
                    }
                })
            );
        private Action<OcrSupSubtitleTaskItem, System.Windows.Controls.ListView> TaskAction =
            async (item, listView) =>
            {
                Paragraph paragraph = item.Paragraph;
                if (!string.IsNullOrEmpty(paragraph.Text))
                {
                    item.Status = vo.TaskStatus.Completed;
                    return;
                }
                Bitmap bitmap = OcrSupSubtitleCommands.Instance.subtitleService.subtitleBitmap(
                    paragraph.Number
                );
                if (bitmap == null)
                {
                    item.Status = vo.TaskStatus.Failed;
                    return;
                }

                OcrSupSubtitleCommands.Instance.ImageDesc = string.Format(
                    "字幕图像 {0}/{1} - {2}x{3}",
                    paragraph.Number + 1,
                    OcrSupSubtitleCommands.Instance.subtitleService.GetSubtitleCount(),
                    bitmap.Width,
                    bitmap.Height
                );
                var itemToScroll = listView.Items[paragraph.Number] as Paragraph;

                System.Windows.Application.Current.Dispatcher.BeginInvoke(
                    new Action(() =>
                    {
                        if (paragraph.Number % 10 == 0)
                        {
                            listView.ScrollIntoView(itemToScroll);
                        }
                    }),
                    DispatcherPriority.Render
                );

                var ocrResult = OcrSupSubtitleCommands.Instance.paddleEngine.DetectText(bitmap);
                var txt = "";
                if (ocrResult.TextBlocks.Count > 0)
                {
                    List<PaddleOCRSharp.TextBlock> list = ocrResult.TextBlocks;
                    if (list.Count == 1)
                    {
                        txt = list[0].Text;
                    }
                    else
                    {
                        for (int i = 0; i < list.Count - 1; i++)
                        {
                            txt += list[i].Text + " ";
                        }
                        txt += list[list.Count - 1].Text;
                    }
                }
                if (OcrSupSubtitleCommands.Instance.TradToSimple)
                {
                    txt = ChineseConverter.Convert(
                        txt,
                        ChineseConversionDirection.TraditionalToSimplified
                    );
                }
                paragraph.Text = txt;
                item.Status = vo.TaskStatus.Completed;
            };

        public ICommand OcrSupFileCommand =>
            _OcrSupFileCommand
            ?? (
                _OcrSupFileCommand = new RelayCommand<object>(o =>
                {
                    if (o == null)
                        return;
                    System.Windows.Controls.ListView listView = (System.Windows.Controls.ListView)o;
                    OcrSupEnable = false;
                    ClearListEnable = false;
                    OcrSupSubtitleTaskQueueManager.Instance.StartOcr = true;
                    OcrSupEnable = true;
                    ClearListEnable = true;
                })
            );

        private void saveSubtitle()
        {
            var format = new SubRip();
            string offsetString = TimeLeave;
            TimeSpan offset = TimeSpan.Parse("00:00:00");
            bool add = true;
            if (!string.IsNullOrWhiteSpace(offsetString))
            {
                offsetString = offsetString.Replace("：", ":");
                if (offsetString.StartsWith("-"))
                {
                    add = false;
                    offsetString = offsetString.Substring(1);
                }
                try
                {
                    offset = TimeSpan.Parse(offsetString);
                }
                catch (Exception ex)
                {
                    var content = new NotificationContent()
                    {
                        Title = "时间偏移量格式错误",
                        Type = NotificationType.Error,
                        Message = "格式(减号开始表示时间前移)\n(-)00:00:00",
                    };
                    AppManager.Instance.NotificationManager.Show(content);
                    return;
                }
                foreach (Paragraph p in SubtitleEncode.Paragraphs)
                {
                    double start = p.StartTime.TotalMilliseconds - offset.TotalMilliseconds;
                    if (add)
                    {
                        start = p.StartTime.TotalMilliseconds + offset.TotalMilliseconds;
                    }
                    TimeCode startTime = new TimeCode(start);
                    p.StartTime = startTime;

                    double end = p.EndTime.TotalMilliseconds - offset.TotalMilliseconds;
                    if (add)
                    {
                        end = p.EndTime.TotalMilliseconds + offset.TotalMilliseconds;
                    }
                    TimeCode endTime = new TimeCode(end);
                    p.EndTime = endTime;
                }
            }
            string srt = Path.ChangeExtension(OcrFile, ".srt");
            var allText = format.ToText(SubtitleEncode, null);
            System.IO.File.WriteAllText(srt, allText, Encoding.UTF8);
        }

        /// <summary>
        /// 检查文件
        /// </summary>
        /// <returns></returns>
        private bool checkFiles()
        {
            if (OcrFile.ToLower().EndsWith("sub"))
            {
                string idxFileName = Path.ChangeExtension(OcrFile, ".idx");
                if (!File.Exists(idxFileName))
                {
                    var content = new NotificationContent()
                    {
                        Title = "无法解析sub文件，原因",
                        Type = NotificationType.Error,
                        Message = OcrFile,
                    };
                    AppManager.Instance.NotificationManager.Show(content);
                    return false;
                }
            }
            return true;
        }

        public void FixShortDisplayTimes(SubtitleEncode subtitle)
        {
            for (int i = 0; i < subtitle.Paragraphs.Count; i++)
            {
                Paragraph p = subtitle.Paragraphs[i];
                if (p.EndTime.TotalMilliseconds <= p.StartTime.TotalMilliseconds)
                {
                    Paragraph next = subtitle.GetParagraphOrDefault(i + 1);
                    double newEndTime =
                        p.StartTime.TotalMilliseconds
                        + new VobSubOcrSettings().DefaultMillisecondsForUnknownDurations;
                    if (next == null || (newEndTime < next.StartTime.TotalMilliseconds))
                    {
                        p.EndTime.TotalMilliseconds = newEndTime;
                    }
                    else
                    {
                        p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - 1;
                    }
                }
            }
        }
    }

    public class VobSubOcrSettings
    {
        public int XOrMorePixelsMakesSpace { get; set; }
        public double AllowDifferenceInPercent { get; set; }
        public double BlurayAllowDifferenceInPercent { get; set; }
        public string LastImageCompareFolder { get; set; }
        public int LastModiLanguageId { get; set; }
        public string LastOcrMethod { get; set; }
        public string TesseractLastLanguage { get; set; }
        public bool UseTesseractFallback { get; set; }
        public bool UseItalicsInTesseract { get; set; }
        public int TesseractEngineMode { get; set; }
        public bool UseMusicSymbolsInTesseract { get; set; }
        public bool RightToLeft { get; set; }
        public bool TopToBottom { get; set; }
        public int DefaultMillisecondsForUnknownDurations { get; set; }
        public bool FixOcrErrors { get; set; }
        public bool PromptForUnknownWords { get; set; }
        public bool GuessUnknownWords { get; set; }
        public bool AutoBreakSubtitleIfMoreThanTwoLines { get; set; }
        public double ItalicFactor { get; set; }

        public bool LineOcrDraw { get; set; }
        public int LineOcrMinHeightSplit { get; set; }
        public bool LineOcrAdvancedItalic { get; set; }
        public string LineOcrLastLanguages { get; set; }
        public string LineOcrLastSpellCheck { get; set; }
        public int LineOcrLinesToAutoGuess { get; set; }
        public int LineOcrMinLineHeight { get; set; }
        public int LineOcrMaxLineHeight { get; set; }
        public int LineOcrMaxErrorPixels { get; set; }
        public string LastBinaryImageCompareDb { get; set; }
        public string LastBinaryImageSpellCheck { get; set; }
        public bool BinaryAutoDetectBestDb { get; set; }
        public string LastTesseractSpellCheck { get; set; }
        public bool CaptureTopAlign { get; set; }
        public int UnfocusedAttentionBlinkCount { get; set; }
        public int UnfocusedAttentionPlaySoundCount { get; set; }
        public string CloudVisionApiKey { get; set; }
        public string CloudVisionLanguage { get; set; }
        public bool CloudVisionSendOriginalImages { get; set; }

        public VobSubOcrSettings()
        {
            XOrMorePixelsMakesSpace = 8;
            AllowDifferenceInPercent = 1.0;
            BlurayAllowDifferenceInPercent = 7.5;
            LastImageCompareFolder = "English";
            LastModiLanguageId = 9;
            LastOcrMethod = "Tesseract";
            UseItalicsInTesseract = true;
            TesseractEngineMode = 3; // Default, based on what is available (T4 docs)
            UseMusicSymbolsInTesseract = true;
            UseTesseractFallback = true;
            RightToLeft = false;
            TopToBottom = true;
            DefaultMillisecondsForUnknownDurations = 5000;
            FixOcrErrors = true;
            PromptForUnknownWords = true;
            GuessUnknownWords = true;
            AutoBreakSubtitleIfMoreThanTwoLines = true;
            ItalicFactor = 0.2f;
            LineOcrLinesToAutoGuess = 100;
            LineOcrMaxErrorPixels = 45;
            LastBinaryImageCompareDb = "Latin+Latin";
            BinaryAutoDetectBestDb = true;
            CaptureTopAlign = false;
            UnfocusedAttentionBlinkCount = 50;
            UnfocusedAttentionPlaySoundCount = 1;
            CloudVisionApiKey = string.Empty;
            CloudVisionLanguage = "en";
            CloudVisionSendOriginalImages = false;
        }
    }
}
